package com.daji.concurrency.multithread_synchronized;


import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

public class B06_Wait_Notify {

}

/*
* TaskQueue 里面有两个方法：
* 第一步：外部方法首先调用getTask，会一直阻塞等待
* 第二步：外部方法调用addTask，会向queue里面添加一个元素。此时getTask就会结束while循环，并return该元素
* 这种操作必须依赖于wait和notify(notifyAll)才能实现，如果注释掉它们，就会失败
* 我们假设注释了它们。外面的方法先调用了getTask, getTask会获得一把锁，这把锁就是this锁，锁住了当前对象（这句话看不懂，回看B04）
* 那么addTask如果和getTask用的是同一个对象（因为锁住了当前对象），就会一直阻塞等待getTask释放掉它的锁。于是就形成了死锁
*
* 如果加上了wait和notify，则是另外一种情形：
* 外部方法先调用getTask，如果queue为空，会调用wait方法，释放掉自己拿到的对象锁，并进入等待状态。
* 然后外部方法调用addTask，由于对象锁被释放掉了，会顺利执行该方法，该方法向queue里面放入了一个元素，随后
* 调用notifyAll。调用了notifyAll之后，getTask会被唤醒，结束它的等待状态，并且重新尝试获得该对象锁
* 调用了notifyAll之后，getTask会被唤醒，结束它的等待状态，并且重新尝试获得该对象锁
* 调用了notifyAll之后，getTask会被唤醒，结束它的等待状态，并且重新尝试获得该对象锁
* 最终，getTask和addTask都会顺利执行完毕
*
* 总结wait :
* 调用wait()方法后，线程进入等待状态，wait()方法不会返回，直到将来某个时刻，线程从等待状态被其他线程唤醒(notify)后，
* wait()方法才会返回，然后，继续执行下一条语句。
* wait方法会释放线程获得的锁.
* wait()方法返回后，线程又会重新试图获得锁。
*
* 总结notify :
* 用来唤醒等待线程（wait）
* notify可以唤醒单个线程，具体唤醒哪一个被wait的线程，由操作系统决定
* notify可以唤醒单个线程，具体唤醒哪一个被wait的线程，由操作系统决定
* 使用notifyAll()将一次性全部唤醒。通常来说，notifyAll()更安全。
*
最终总结：
在synchronized内部可以调用wait()使线程进入等待状态；
必须在已获得的锁对象上调用wait()方法；
已唤醒的线程还需要重新获得锁后才能继续执行。

在synchronized内部可以调用notify()或notifyAll()唤醒其他等待线程；
必须在已获得的锁对象上调用notify()或notifyAll()方法；

 * */
class TaskQueue {
    Queue<String> queue = new LinkedList<>();

    public synchronized void addTask(String s) {
        this.queue.add(s);
        this.notifyAll();
    }

    public synchronized String getTask() throws InterruptedException {
        while (queue.isEmpty()) {
            this.wait();
        }
        return queue.remove();
    }
}
